﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml.Lite.Core.Office;
using DFS = DocumentFormat.OpenXml.Lite.Core.DocumentFS;
using DocumentFormat.OpenXml.Lite.Core.Relationship;
using DocumentFormat.OpenXml.Lite.Core.ContentType;
using Base = DocumentFormat.OpenXml.Lite.PresentationML.Presentation.Base;
using DocumentFormat.OpenXml.Lite.PresentationML.Presentation.Base;
using DocumentFormat.OpenXml.Lite.Core;
using DocumentFormat.OpenXml.Lite.DrawingML.Style;
using System.Reflection;
using DocumentFormat.OpenXml.Lite.Shared;
using DocumentFormat.OpenXml.Lite.Helper;

namespace DocumentFormat.OpenXml.Lite.PresentationML
{
    internal class PresentationDocument : DocumentBase
    {
        private Random random = new Random();
        private int masters = 0, layouts = 0, slides = 0, themes = 0, charts = 0, images = 0;

        public Base.Presentation Presentation { get; private set; }

        public PresentationDocument(Guid id, DocumentType type)
            : base(id, type)
        {
        }

        internal int NextInt()
        {
            return random.Next();
        }

        internal int NextInt(int min, int max)
        {
            return random.Next(min, max);
        }

        public override bool ExtractFile(string file, Stream content)
        {
            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            if (file == @"[Content_Types].xml")
            {
                ContentTypeManager.Deserialize(content);
                return true;
            }
            else if (file == @"_rels\.rels" || file == @"_rels/.rels")
            {
                var relManager = sandbox.GetDirectory("_rels").GetFile(".rels").Object as RelationshipManager;
                relManager.Deserialize(content);
                return true;
            }
            else if (file == @"ppt\presentation.xml" || file == @"ppt/presentation.xml")
            {
                Presentation.Deserialize(content);
                return true;
            }
            else if (file.StartsWith(@"ppt\slideMasters\slideMaster") || file.StartsWith(@"ppt/slideMasters/slideMaster"))
            {
                SlideMaster master = new SlideMaster();
                master.Deserialize(content);

                sandbox.CreateFile(file.Replace('\\', '/'), master);
                return true;
            }
            else if (file.StartsWith(@"ppt\slideLayouts\slideLayout") || file.StartsWith(@"ppt/slideLayouts/slideLayout"))
            {
                SlideLayout layout = new SlideLayout();
                layout.Deserialize(content);

                sandbox.CreateFile(file.Replace('\\', '/'), layout);
                return true;
            }
            else if (file.StartsWith(@"ppt\slides\slide") || file.StartsWith(@"ppt/slides/slide"))
            {
                Slide slide = new Slide();
                slide.Deserialize(content);

                sandbox.CreateFile(file.Replace('\\', '/'), slide);
                return true;
            }
            else if (file.StartsWith(@"ppt\theme\theme") || file.StartsWith(@"ppt/theme/theme"))
            {
                Theme theme = new Theme();
                theme.Deserialize(content);

                sandbox.CreateFile(file.Replace('\\', '/'), theme);
                return true;
            }
            else if (file.StartsWith(@"word\charts\chart") || file.StartsWith(@"word/charts/chart"))
            {
                charts++;
                System.Type t = System.Type.GetType("DocumentFormat.OpenXml.Lite.DrawingML.Helper, DocumentFormat.OpenXml.Lite.DrawingML");
                if (t != null)
                {
                    MethodInfo method = t.GetMethod("ExtractChart", BindingFlags.Static | BindingFlags.Public);
                    method.Invoke(null, new object[] { file, content, sandbox });

                    return true;
                }
            }
            else if (file.StartsWith(@"word\media\") || file.StartsWith(@"word/media/"))
            {
                ImageType type = ImageType.None;
                string extension = file.Split('.')[1];
                switch (extension)
                {
                    case "bmp":
                        type = ImageType.BMP;
                        break;
                    case "gif":
                        type = ImageType.GIF;
                        break;
                    case "jpeg":
                        type = ImageType.JPEG;
                        break;
                    case "jpg":
                        type = ImageType.JPG;
                        break;
                    case "png":
                        type = ImageType.PNG;
                        break;
                    case "tiff":
                        type = ImageType.TIFF;
                        break;
                    default: break;
                }

                if (type == ImageType.None)
                {
                    return false;
                }

                images++;
                Image img = new Image(null, type);
                img.Deserialize(content);

                sandbox.CreateFile(file.Replace('\\', '/'), img);

                return true;
            }
            if (file.EndsWith(@".rels"))
            {
                if (file == @"ppt\_rels\presentation.xml.rels" || file == @"ppt/_rels/presentation.xml.rels")
                {
                    var relManager = sandbox.GetFile(file.Replace('\\', '/')).Object as RelationshipManager;
                    relManager.Deserialize(content);
                }
                else
                {
                    var relManager = new RelationshipManager();
                    relManager.Deserialize(content);
                    sandbox.CreateFile(file.Replace('\\', '/'), relManager);
                }

                return true;
            }

            return base.ExtractFile(file, content);
        }

        public override void CreateDefault()
        {
            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);

            DFS.Directory relDir = sandbox.CreateDirectory("_rels");
            DFS.Directory pptDir = sandbox.CreateDirectory("ppt");

            Presentation = new Base.Presentation(this);
            DFS.File pptXml = pptDir.CreateFile("presentation.xml", Presentation);

            RelationshipManager relManager = new RelationshipManager();
            DFS.File relFile = relDir.CreateFile(".rels", relManager);
            relManager.CreateRelationship(RelationshipURI.OfficeDocument, pptXml);

            RelationshipManagers.Add(relManager);

            ContentTypeManager = new ContentTypeManager();
            DFS.File contFile = sandbox.CreateFile("[Content_Types].xml", ContentTypeManager);

            ContentTypeManager.CreateDefault(ContentType.Xml, ExtensionType.Xml);
            ContentTypeManager.CreateDefault(ContentType.Rels, ExtensionType.Rels);
            ContentTypeManager.CreateOverride(ContentType.Presentation, pptXml);

            DFS.Directory slideDir = pptDir.CreateDirectory("slides");
            slideDir.CreateDirectory("_rels");

            DFS.Directory masterDir = pptDir.CreateDirectory("slideMasters");
            masterDir.CreateDirectory("_rels");

            DFS.Directory layoutDir = pptDir.CreateDirectory("slideLayouts");
            layoutDir.CreateDirectory("_rels");

            pptDir.CreateDirectory("theme");

            RelationshipManager relManager2 = new RelationshipManager();
            RelationshipManagers.Add(relManager2);
            pptDir.CreateDirectory("_rels").CreateFile("presentation.xml.rels", relManager2);
        }

        public SlideMaster CreateSlideMaster(out string relId)
        {
            masters++;

            SlideMaster master = new SlideMaster();

            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            DFS.File masterFile = sandbox.GetDirectory("ppt/slideMasters").CreateFile("slideMaster" + masters.ToString() + ".xml", master);

            var cManager = sandbox.GetFile("[Content_Types].xml").Object as ContentTypeManager;
            cManager.CreateOverride(ContentType.SlideMaster, masterFile);

            var relManager = sandbox.GetFile("ppt/_rels/presentation.xml.rels").Object as RelationshipManager;
            int id = relManager.CreateRelationship(RelationshipURI.SlideMaster, masterFile, sandbox.GetDirectory("ppt"));
            relId = RelationshipManager.GetRelationshipId(id);

            return master;
        }

        public Slide CreateSlide(SlideLayout layout, out string relId)
        {
            slides++;

            Slide slide = new Slide();

            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            DFS.File slideFile = sandbox.GetDirectory("ppt/slides").CreateFile("slide" + slides.ToString() + ".xml", slide);

            var cManager = sandbox.GetFile("[Content_Types].xml").Object as ContentTypeManager;
            cManager.CreateOverride(ContentType.Slide, slideFile);

            var relManager = sandbox.GetFile("ppt/_rels/presentation.xml.rels").Object as RelationshipManager;
            var id = relManager.CreateRelationship(RelationshipURI.Slide, slideFile, sandbox.GetDirectory("ppt"));
            relId = RelationshipManager.GetRelationshipId(id);

            var layoutFile = sandbox.GetFilesRecursive().FirstOrDefault(f => f.Object == layout);
            var relManager2 = new RelationshipManager();
            relManager2.CreateRelationship(RelationshipURI.SlideLayout, layoutFile, slideFile.Parent);
            RelationshipManagers.Add(relManager2);
            if (!sandbox.GetDirectory("ppt/slides").ExistsDirectory("_rels"))
            {
                sandbox.GetDirectory("ppt/slides").CreateDirectory("_rels");
            }
            sandbox.GetDirectory("ppt/slides/_rels").CreateFile("slide" + slides.ToString() + ".xml.rels", relManager2);

            return slide;
        }

        public SlideLayout CreateLayout(SlideMaster master)
        {
            layouts++;

            SlideLayout slideLayout = new SlideLayout();

            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            DFS.File layoutFile = sandbox.GetDirectory("ppt/slideLayouts")
                .CreateFile("slideLayout" + layouts.ToString() + ".xml", slideLayout);

            var cManager = sandbox.GetFile("[Content_Types].xml").Object as ContentTypeManager;
            cManager.CreateOverride(ContentType.SlideLayout, layoutFile);

            DFS.File masterFile = sandbox.GetFilesRecursive().FirstOrDefault(f => f.Object == master);
            if (masterFile == null)
                throw new OpenXmlException("Invalid slide master!");

            string masterRelFile = masterFile.Parent.GetXmlPath() + "_rels/" + masterFile.Name + ".rels";
            if (!sandbox.ExistsFile(masterRelFile))
            {
                sandbox.CreateFile(masterRelFile, new RelationshipManager());
            }
            var relManager = sandbox.GetFile(masterRelFile).Object as RelationshipManager;
            var id = relManager.CreateRelationship(RelationshipURI.SlideLayout, layoutFile, masterFile.Parent);
            master.SlideLayoutIds.Add(new SimpleIdElement()
            {
                Id = (uint)(NextInt()) + int.MaxValue + 1,
                RelationshipId = RelationshipManager.GetRelationshipId(id)
            });

            var relManager2 = new RelationshipManager();
            relManager2.CreateRelationship(RelationshipURI.SlideMaster, masterFile, layoutFile.Parent);
            RelationshipManagers.Add(relManager2);
            if (!sandbox.GetDirectory("ppt/slideLayouts").ExistsDirectory("_rels"))
            {
                sandbox.GetDirectory("ppt/slideLayouts").CreateDirectory("_rels");
            }
            sandbox.GetDirectory("ppt/slideLayouts/_rels").CreateFile("slideLayout" + layouts.ToString() + ".xml.rels", relManager2);

            return slideLayout;
        }

        
        public Theme CreateTheme(SlideMaster master)
        {
            themes++;

            Theme theme = new Theme();

            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            DFS.File themeFile = sandbox.GetDirectory("ppt/theme").CreateFile("theme" + themes.ToString() + ".xml", theme);

            var cManager = sandbox.GetFile("[Content_Types].xml").Object as ContentTypeManager;
            cManager.CreateOverride(ContentType.Theme, themeFile);

            var relManager = sandbox.GetFile("ppt/_rels/presentation.xml.rels").Object as RelationshipManager;
            var id = relManager.CreateRelationship(RelationshipURI.Theme, themeFile, sandbox.GetDirectory("ppt"));
            
            DFS.File masterFile = sandbox.GetFilesRecursive().FirstOrDefault(f => f.Object == master);
            if (masterFile == null)
                throw new OpenXmlException("Invalid slide master!");

            string masterRelFile = masterFile.Parent.GetXmlPath() + "_rels/" + masterFile.Name + ".rels";
            if (!sandbox.ExistsFile(masterRelFile))
            {
                sandbox.CreateFile(masterRelFile, new RelationshipManager());
            }
            var relManager2 = sandbox.GetFile(masterRelFile).Object as RelationshipManager;
            relManager2.CreateRelationship(RelationshipURI.Theme, themeFile, masterFile.Parent);

            return theme;
        }
    }
}
